﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Practices.ServiceLocation;
using Pap.Data;
using Pap.Data.Abstraction.Utils;
using Pap.Core.Utilities;
using Pap.Services.WorkflowEngine;
using Utils;

namespace Pap.Services.FinancialServices
{
    /// <summary>
    /// 
    /// </summary>
    public class FinanceService : IFinanceService
    {
        /// <summary>
        /// 
        /// </summary>
        IDatabase _Database;

        private readonly IWorkFlow _workFlow;
        private readonly IWorkflowService _workflowService;

        /// <summary>
        /// Initializes a new instance of the <see cref="FinanceService"/> class.
        /// </summary>
        /// <param name="database">The database.</param>
        /// <param name="workFlow"></param>
        /// <param name="workflowService"></param>
        public FinanceService(IDatabase database, IWorkFlow workFlow, IWorkflowService workflowService)
        {
            _Database = database;
            _workFlow = workFlow;
            _workflowService = workflowService;
        }

        /// <summary>
        /// Gets the committee balance.
        /// </summary>
        /// <param name="committeeID">The committee ID.</param>
        /// <returns></returns>
        public double GetCommitteeBalance(Guid committeeID)
        {
            var committee = _Database.Committees.Single(com => com.CommitteeID == committeeID);
            return (from account in committee.CommitteeBankAccounts
                   select account.Balance).Sum().GetValueOrDefault(0);
        }

        /// <summary>
        /// Gets the main committee balance.
        /// </summary>
        /// <returns></returns>
        public double GetMainCommitteeBalance()
        {
            double temp = 0;

            var mainCommittee = _Database.Committees.FirstOrDefault(comm => comm.IsMainCommittee == true);
            if (mainCommittee == null)
                return 0;
            var mainCommitteeAccounts = mainCommittee.CommitteeBankAccounts;

            var deposites = _Database.MainCommitteeDeposits.Where(depo => mainCommitteeAccounts.Select(acc => acc.CommitteeBankAccountID).Contains(depo.CommitteBankAccountID)).Select(
                depo => depo.Amount);
            if (deposites.Count() > 0)
                temp += deposites.Sum();

            var expenses = mainCommitteeAccounts.SelectMany(acc => acc.CommitteeBankAccountExpenses).Select(
                expen => expen.WithdrawAmount);
            if (expenses.Count() > 0)
                temp -= expenses.Sum();

            var mainExpenses = _Database.MainCommitteeExpenses.Where(mainComEx => mainCommitteeAccounts.Select(cA => cA.CommitteeBankAccountID).Contains(mainComEx.CommitteBankAccountID)).Select(
                manEx => manEx.Amount);
            if (mainExpenses.Count() > 0)
                temp -= mainExpenses.Sum();

            var committeesDeposits = _Database.CommitteeBankAccountMainBranchDeposites.Select(depo => depo.DepositAmount);
            if (committeesDeposits.Count() > 0)
                temp -= committeesDeposits.Sum();

            return temp;
        }

        /// <summary>
        /// Gets the bank account balance.
        /// </summary>
        /// <param name="bankAccount">The bank account.</param>
        /// <returns></returns>
        public double GetBankAccountBalance(ICommitteeBankAccount bankAccount)
        {
#warning need to be removed
            //double mainbranchDeposites = bankAccount.CommitteeBankAccountMainBranchDeposites.Select(depo => depo.DepositAmount).Sum();
            return bankAccount.Balance.GetValueOrDefault();
        }

        /// <summary>
        /// Associations the deposit bank.
        /// </summary>
        /// <param name="depositAmount">The deposit amount.</param>
        /// <param name="committeeID">The committee ID.</param>
        /// <param name="projectID">The project ID.</param>
        /// <param name="applicationNumber">The application number.</param>
        public void AssociationDepositBank(double depositAmount, Guid committeeID, Guid projectID, string applicationNumber)
        {
            var comBankAccount = _Database.CommitteeBankAccounts.Where(bankAccount => bankAccount.CommitteeID == committeeID && bankAccount.ProjectID == projectID).First();
            ICommitteeBankAccountDeposite committeeDeposite = ServiceLocator.Current.GetInstance<ICommitteeBankAccountDeposite>();
            committeeDeposite.CommitteeBankAccountDepositID = Guid.NewGuid();
            // committeeDeposite.
        }

        /// <summary>
        /// Unions the deposit committee.
        /// </summary>
        /// <param name="depositAmount">The deposit amount.</param>
        /// <param name="committeeID">The committee ID.</param>
        /// <param name="operationNumber">The operation number.</param>
        /// <param name="bankAccountNumberID">The bank account number ID.</param>
        /// <param name="depositDate">The deposit date.</param>
        /// <returns></returns>
        public Guid UnionDepositCommittee(double depositAmount, Guid committeeID, string operationNumber, Guid bankAccountNumberID, DateTime depositDate)
        {
            ICommitteeBankAccountMainBranchDeposite mainDeposit = ServiceLocator.Current.GetInstance<ICommitteeBankAccountMainBranchDeposite>();

            mainDeposit.CommitteeBankAccountMainBranchDepositID = Guid.NewGuid();
            mainDeposit.CommitteeBankAccountID = bankAccountNumberID;
            mainDeposit.DepositAmount = depositAmount;
            mainDeposit.DepositDate = depositDate;
            mainDeposit.OperationNumber = operationNumber;

            _Database.Insert<ICommitteeBankAccountMainBranchDeposite>(mainDeposit);
            return mainDeposit.CommitteeBankAccountMainBranchDepositID;
        }

        /// <summary>
        /// Gets the paid reciepts.
        /// </summary>
        /// <param name="projectID">The project ID.</param>
        /// <returns></returns>
        public IEnumerable<IReceipt> GetPaidReciepts(Guid projectID)
        {
            var depositReciptesIDs = _Database.CommitteeBankAccountDepositReceipts.Select(comBankDepositReciept => comBankDepositReciept.Receipt.ReceiptID);

            var paymentReceipts = _Database.LoanInstallmentPayments.Where(
                l => l.LoanExecutionInstallment.LoanExecution.CustomerLoan.ProjectStage.ProjectID.Equals(projectID)).Select(
                payment => payment.ReceiptID);
            var projectReceipts = _Database.CustomerLoans.Where(loan => loan.ProjectStage.ProjectID == projectID).Select(loan => loan.ReceiptID);

            var payedReceipts = _Database.Receipts.Where(rec => (paymentReceipts.Contains(rec.ReceiptID) || projectReceipts.Contains(rec.ReceiptID))
                && !depositReciptesIDs.Contains(rec.ReceiptID));

            return payedReceipts;
        }

        public IEnumerable<ILoanExecutionInstallment> GetAllDuesForCommittee(Guid committeeId)
        {
            return from installmet in _Database.LoanExecutionInstallments
                   where !installmet.IsPaid &&
                         _workflowService.GetInStep(Steps.PaymentList).Contains(installmet.LoanExecution.LoanID) &&
                         installmet.LoanExecution.CustomerLoan.CommitteeID == committeeId &&
                         (from loanExecution in _Database.LoanExecutions
                          where loanExecution.LoanID == installmet.LoanExecution.LoanID
                          orderby loanExecution.ExecutionDate descending
                          select loanExecution
                         ).Take(1).Single().LoanExecutionID == installmet.LoanExecutionID
                   select installmet;
        }

        public IEnumerable<ILoanExecutionInstallment> GetDuesForCommitteeByDate(Guid committeeId, DateTime startDate, DateTime endDate)
        {
            return from installmet in _Database.LoanExecutionInstallments
                   where !installmet.IsPaid &&
                         installmet.InstallmentDate.Date >= startDate.Date &&
                         installmet.InstallmentDate.Date <= endDate.Date &&
                         _workflowService.GetInStep(Steps.PaymentList).Contains(installmet.LoanExecution.LoanID) &&
                         installmet.LoanExecution.CustomerLoan.CommitteeID == committeeId &&
                         (from loanExecution in _Database.LoanExecutions
                          where loanExecution.LoanID == installmet.LoanExecution.LoanID
                          orderby loanExecution.ExecutionDate descending
                          select loanExecution
                         ).Take(1).Single().LoanExecutionID == installmet.LoanExecutionID
                   select installmet;
        }

        public IEnumerable<ILoanExecutionInstallment> GetTodayDuesForCommittee(Guid committeeId)
        {
            return from installmet in _Database.LoanExecutionInstallments
                   where !installmet.IsPaid &&
                         installmet.InstallmentDate.Date == DateTime.Today &&
                         _workflowService.GetInStep(Steps.PaymentList).Contains(installmet.LoanExecution.LoanID) &&
                         installmet.LoanExecution.CustomerLoan.CommitteeID == committeeId &&
                         (from loanExecution in _Database.LoanExecutions
                          where loanExecution.LoanID == installmet.LoanExecution.LoanID
                          orderby loanExecution.ExecutionDate descending
                          select loanExecution
                         ).Take(1).Single().LoanExecutionID == installmet.LoanExecutionID
                   select installmet;
        }

        public IEnumerable<ILoanExecutionInstallment> GetLateDuesForCommittee(Guid committeeId)
        {
            return from installmet in _Database.LoanExecutionInstallments
                   where !installmet.IsPaid &&
                         installmet.InstallmentDate.Date < DateTime.Today &&
                         _workflowService.GetInStep(Steps.PaymentList).Contains(installmet.LoanExecution.LoanID) &&
                         installmet.LoanExecution.CustomerLoan.CommitteeID == committeeId &&
                         (from loanExecution in _Database.LoanExecutions
                          where loanExecution.LoanID == installmet.LoanExecution.LoanID
                          orderby loanExecution.ExecutionDate descending
                          select loanExecution
                         ).Take(1).Single().LoanExecutionID == installmet.LoanExecutionID
                   select installmet;
        }
    }
}
